/**
 * 
 */
package com.gragra.util;
import java.util.Iterator;
import it.unimi.dsi.fastutil.ints.Int2DoubleOpenHashMap;
import it.unimi.dsi.fastutil.ints.IntIterator;
import it.unimi.dsi.fastutil.longs.Long2DoubleMap.Entry;
import it.unimi.dsi.fastutil.longs.Long2DoubleOpenHashMap;
import it.unimi.dsi.fastutil.longs.LongIterator;
/**
 * @author Christoph Teichmann
 * created Jun 29, 2013 6:53:29 PM
 * @version 0.1
 */
public class OpenUnitElementCounter
{
	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString()
	{
		return "OpenUnitElementCounter [sum=" + this.sum + ", counter="
				+ mapBack(this.counter) + "]";
	}
	/**
	 * 
	 * @param counter2
	 * @return
	 */
	private static String mapBack(Long2DoubleOpenHashMap counter2)
	{
		StringBuilder sb = new StringBuilder();
		sb.append("{");
		LongIterator iit = counter2.keySet().iterator();
		while(iit.hasNext())
		{
			long l = iit.nextLong();
			sb.append(" <");
			sb.append(BooleanInterweave.getFirst(l));
			sb.append(",");
			sb.append(BooleanInterweave.getSecond(l));
			sb.append("> => ");
			sb.append(counter2.get(l));
		}
		sb.append(" }");
		return sb.toString();
	}
	/**
	 * 
	 */
	private final Int2DoubleOpenHashMap sum = new Int2DoubleOpenHashMap();
	/**
	 * 
	 * @return
	 */
	public IntIterator getUnitIterator()
	{return this.sum.keySet().iterator();}
	/**
	 * 
	 *
	 * @param unit
	 * @return
	 */
	public double getSum(int unit)
	{return sum.get(unit);}
	/**
	 * 
	 */
	private final Long2DoubleOpenHashMap counter = new Long2DoubleOpenHashMap();
	/**
	 * 
	 */
	public void clear()
	{
		this.sum.clear();
		this.counter.clear();
	}
	/**
	 * 
	 *
	 * @param unit
	 * @param element
	 * @return
	 */
	public double getValue(int unit,int element)
	{return this.counter.get(BooleanInterweave.combine(unit, element));}
	/**
	 * 
	 *
	 * @param unit
	 * @param element
	 * @param amount
	 * @return
	 */
	public double add(int unit,int element,double amount)
	{
		this.counter.addTo(BooleanInterweave.combine(unit, element), amount);
		this.sum.addTo(unit, amount);
		return amount;
	}
	/**
	 * 
	 *
	 * @param unit
	 * @param element
	 * @param value
	 * @return
	 */
	public double set(int unit,int element,double value)
	{
		long code = BooleanInterweave.combine(unit, element);
		this.sum.addTo(unit,value - counter.get(code));
		this.counter.put(code, value);
		return value;
	}
	/**
	 * 
	 *
	 * @param other
	 */
	public void add(OpenUnitElementCounter other)
	{
		for(Entry ent : other.counter.long2DoubleEntrySet())
		{
			long code = ent.getLongKey();
			int unit = BooleanInterweave.getFirst(code);
			double val = ent.getDoubleValue();
			this.counter.addTo(code, val);
			this.sum.addTo(unit, val);
		}
	}
	/**
	 * 
	 *
	 * @param other
	 */
	public void subtract(OpenUnitElementCounter other)
	{
		for(Entry ent : other.counter.long2DoubleEntrySet())
		{
			long code = ent.getLongKey();
			int unit = BooleanInterweave.getFirst(code);
			double val = ent.getDoubleValue();
			this.counter.addTo(code, -val);
			this.sum.addTo(unit, -val);
		}
	}
	/**
	 * 
	 * @return
	 */
	public MappingIterator getIterator()
	{return new MappingIterator();}
	/**
	 * 
	 * @author Christoph Teichmann
	 * 30.06.2013
	 */
	public class MappingIterator
	{
		/**
		 * 
		 */
		private final Iterator<Entry> it = counter.long2DoubleEntrySet().fastIterator();
		/**
		 * 
		 */
		private Entry ent = null;
		/**
		 * @return
		 * @see java.util.Iterator#hasNext()
		 */
		public boolean hasNext()
		{return this.it.hasNext();}

		/**
		 * 
		 */
		public void setToNext()
		{ent = this.it.next();}
		/**
		 * 
		 * @return
		 */
		public int getCurrentUnit()
		{return BooleanInterweave.getFirst(ent.getLongKey());}
		/**
		 * 
		 * @return
		 */
		public int getCurrentElement()
		{return BooleanInterweave.getSecond(ent.getLongKey());}
		/**
		 * 
		 * @return
		 */
		public double getCurrentValue()
		{return ent.getDoubleValue();}
		/**
		 * 
		 * @return
		 */
		public double getCurrentUnitSum()
		{return sum.get(this.getCurrentUnit());}
	}
}